/*****************************************************************************
 * $LastChangedDate: 2011-09-10 18:51:54 -0400 (Sat, 10 Sep 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 *//*
 * LEGAL:   COPYRIGHT (C) 2005 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef TEST_HH
#define TEST_HH

#include <unistd.h>

//==============================================================================
// !! Define TEST_SHPTR_YIELD() before the base module defines it as a NOP !!
// shptr can use this macro for stress tests.
#if COMPILE_THREADS
    #include "threads/macros.hh"  // must define THREAD_YIELD() first
    #ifdef THREAD_YIELD
    #   define TEST_SHPTR_YIELD() THREAD_YIELD()
    #else
    #   error "TEST_SHPTR_YIELD must be defined (not a NOP) to stress-test the threads module."
    #endif
#else
    #define TEST_SHPTR_YIELD()
#endif
//==============================================================================

// Don't tell Bjarne ;-)
#define private public

#ifndef VERBOSE
#define VERBOSE  1
#endif

#if VERBOSE
#define VERBOSE_CODE( STREAM_CODE ) STREAM_CODE
#else
#define VERBOSE_CODE( STREAM_CODE )
#endif

// To test test internal functions.
#define INTERN
#define INTERNAL

//==============================================================================

// Use this macro in main() of every test.
// base::gGlobals.mTesting affects how assertions and exceptions are raised.
enum { TEST_ERROR_MODE_CATCH = false,    // means that a test normally expects an exception and will catch them
       TEST_ERROR_MODE_ABORT = true };   // means to call abort(), to trap to debugger, if a test fails
#define RUN_TEST( NAME, TEST_ERROR_MODE, FUNCS_AND_FUNCS )          \
    base::gGlobals.mTesting = true;                                 \
    /* Show base prefix such as hex 0x */                           \
    COUT.setf( std::ios::showbase );                                \
    CERROR.setf( std::ios::showbase );                              \
    CLOG.setf( std::ios::showbase );                                \
    /* ASSERT() shoul call abort() for debugger. */                 \
    base::gGlobals.mAssertAbort =                                   \
        (TEST_ERROR_MODE == TEST_ERROR_MODE_ABORT);                 \
    CDEBUG << "----------------------------------" << std::endl     \
           << "Running test " << (NAME)            << std::endl     \
           << "----------------------------------" << std::endl;    \
    try                                                             \
    {                                                               \
        if ( FUNCS_AND_FUNCS )                                      \
        {                                                           \
            CDEBUG << "Test PASSED." << std::endl;                  \
            exit( 0 );                                              \
        }                                                           \
        else                                                        \
        {                                                           \
            CDEBUG << "Test FAILED!" << std::endl;                  \
            exit( 1 );                                              \
        }                                                           \
    }                                                               \
    catch ( const std::exception& ex )                              \
    {                                                               \
        CDEBUG << "** EXCEPTION!! ** " << ex.what()                 \
               << "Test FAILED!" << std::endl;                      \
        exit( 1 );                                                  \
    }                                                               \
    catch (...)                                                     \
    {                                                               \
        CDEBUG << "** EXCEPTION!! **"                               \
               << "Test FAILED!" << std::endl;                      \
        exit( 1 );                                                  \
    }

//==============================================================================

#include "base/module.hh"
#include "base/stream.hh"
#include "base/globals.hh"
using namespace base;
#if COMPILE_THREADS
#include "threads/module.hh"
#include "threads/macros.hh"
using namespace threads;
#endif

//==============================================================================

/*****************************************************************************
 * __LINE__ is apparently an int, not a string.
 *
 * Example:
 * if ( cond ) return Fail();
 *****************************************************************************/
bool
Fail_( int line )
{
    CDEBUG << "FAILED at line=" << line << std::endl;
    _exit( 1 );  // really exit
    return false;
}
#define Fail() Fail_( __LINE__ )

//==============================================================================

#include <signal.h>

int sAlarmExitCode = 0;

void
TestSignalAlarm( int signum )
{
    CDEBUG << "-- SIGARLM (test ran too long) --" << std::endl
           << "-- exit code will be " << sAlarmExitCode << std::endl;
    exit( sAlarmExitCode );
}

// Install alarm signal to stop test if it runs too long.
void
SET_TEST_ALARM_SECONDS( const uint seconds, const int alarmExitCode = 0 )
{
    sAlarmExitCode = alarmExitCode;
    signal( SIGALRM, TestSignalAlarm );
    alarm( seconds );
}

//==============================================================================
// Every threads test should check TEST_SHPTR_YIELD() else tests are useless!
#include "check_TEST_SHPTR_YIELD.hh"
//==============================================================================

#endif // TEST_HH
